package com.zgg.utils;

import com.alibaba.fastjson.JSONObject;
import com.aliyun.oss.common.utils.BinaryUtil;
import com.zgg.config.AliOssProperties;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URI;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

@Component
public class CallbackUtils {
    private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(CallbackUtils.class);
    @Autowired
    private RedisUtils redisUtils;

    /**
     * 对客户端进行回调
     *
     * @param clientCallBackBody
     */
    public void clientCallBack(String clientCallBackBody, String filename, String fileId) {
        logger.info("=====================>>>>>>>>   clientCallBackBody：【" + clientCallBackBody + "】filename：【" + filename + "】fileId：【" + fileId + "】");
        long startTime = System.currentTimeMillis();


        int i = 1;
        if (!StringUtils.isEmpty(clientCallBackBody)) {
            String content = BASE64Utils.decode(clientCallBackBody);
            logger.info("=========>>>>>>>      clientCallBackBody 反编码后的结果：【" + content + "】");

            JSONObject clientCallBackJson = null;
            try {
                clientCallBackJson = JSONObject.parseObject(content);
            } catch (Exception ex) {
                logger.error("============>>>>>>   clientCallBackBody转换json失败");
            }
            if (clientCallBackJson != null && clientCallBackJson.containsKey("url")) {
                String url = clientCallBackJson.getString("url");
                Map<String, String> argsMap = new HashMap<>();
                argsMap.put("filename", filename);
                argsMap.put("fileid", fileId);
                if (clientCallBackJson.containsKey("args")) {
                    JSONObject argsJson = clientCallBackJson.getJSONObject("args");
                    Set<String> keySet = argsJson.keySet();
                    for (String key : keySet) {
                        argsMap.put(key, argsJson.getString(key));
                    }
                }
                while (i <= 5) {
                    logger.info("============>>>>>>  url：【" + url + "】第【" + i + "】次回调!");
                    JSONObject resultJson = HttpClientUtils.postForEntity(url, argsMap);
                    if (resultJson.getInteger("code") != 200) {
                        logger.info("============>>>>>>>   url：【" + url + "】第【" + i + "】次回调失败！");
                        i++;
                    } else {
                        logger.info("============>>>>>>>   回调成功！");
                        //删除上传临时文件
                        FileUtils.removeFilePath(filename);
                        //删除缓存
                        redisUtils.delete(AliOssProperties.uploadFileCache + filename);
                        i = -1;
                        break;
                    }
                }
                if (i > 5) {
                    // TODO: 2018/11/13 错误需要记录，并有重试机制
                    logger.error("==========>>>>>>>>     回调接口【" + url + "】回调重试次数已经达到5次，请检查接口！");
                    //删除缓存
                    redisUtils.delete(AliOssProperties.uploadFileCache + filename);
                    logger.warn("======>>>>   redis key 【" + AliOssProperties.uploadFileFailureCache + filename + "】  value:【" + fileId + "||" + clientCallBackBody + "】");
                    redisUtils.set(AliOssProperties.uploadFileFailureCache + filename, fileId + "||" + clientCallBackBody);
                    redisUtils.incr(AliOssProperties.uploadFileFailureCache + filename + "_count", 1);
                } else {
                    logger.info("==========>>>>>>>>     回调接口【" + url + "】成功，请求次数【" + i + "】次！");
                }
            }
        }
        long endTime = System.currentTimeMillis();
        logger.error("=============>>>>>>        client callback响应时间：【" + (endTime - startTime) + "】ms");
    }


    /**
     * 检验是否是oss服务器发送过来的回调请求
     *
     * @param authorizationInput base64编码后的签名
     * @param pubKeyInput        base64编码手的公钥地址
     * @param requestBody        请求body体
     * @param queryString        请求的查询参数
     * @param uri                请求的URI
     * @return 如果校验成功则返回true，否则返回false。
     */
    public static final boolean verifyOSSCallbackRequest(String authorizationInput, String pubKeyInput, String requestBody, String queryString, String uri) {

        byte[] publicKeyBytes = BinaryUtil.fromBase64String(pubKeyInput);
        String publicKeyAddr = new String(publicKeyBytes);

        String publicKey = executeGet(publicKeyAddr);
        if (StringUtils.isEmpty(publicKey)) {
            return false;
        }

        publicKey = publicKey.replace("-----BEGIN PUBLIC KEY-----", "");
        publicKey = publicKey.replace("-----END PUBLIC KEY-----", "");

        StringBuffer requestContent = new StringBuffer();

        requestContent.append(uri).append("?").append(queryString).append("\n").append(requestBody);

        byte[] sign = BinaryUtil.fromBase64String(authorizationInput);

        return doCheck(requestContent.toString(), sign, publicKey);


    }

    /**
     * 检查公钥和数字签名是否正确
     *
     * @param content   需要检查的内容
     * @param sign      数字签名
     * @param publicKey 公钥
     * @return
     */
    public static final boolean doCheck(String content, byte[] sign, String publicKey) {
        try {
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            byte[] encodedKey = BinaryUtil.fromBase64String(publicKey);
            PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));
            java.security.Signature signature = java.security.Signature.getInstance("MD5withRSA");
            signature.initVerify(pubKey);
            signature.update(content.getBytes());
            boolean bverify = signature.verify(sign);
            return bverify;

        } catch (Exception e) {
            e.printStackTrace();
        }

        return false;
    }

    /**
     * 想AliyunOSS服务器发起请求，获得结果用于检查是否是服务器发起的Callback请求
     *
     * @param url AliyunOSS服务器路径
     * @return 请求结果
     */
    private static final String executeGet(String url) {
        BufferedReader in = null;

        String content = null;
        try {
            // 定义HttpClient
            @SuppressWarnings("resource")
            DefaultHttpClient client = new DefaultHttpClient();
            // 实例化HTTP方法
            HttpGet request = new HttpGet();
            request.setURI(new URI(url));
            HttpResponse response = client.execute(request);

            in = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
            StringBuffer sb = new StringBuffer("");
            String line = "";
            String NL = System.getProperty("line.separator");
            while ((line = in.readLine()) != null) {
                sb.append(line + NL);
            }
            in.close();
            content = sb.toString();
        } catch (Exception e) {
            return null;
        } finally {
            if (in != null) {
                try {
                    in.close();// 最后要关闭BufferedReader
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            return content;
        }
    }
}
